﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MethodWorx.Core.DomainModel;
using MethodWorx.CMS.Providers;

namespace MethodWorx.CMS.Plugins
{
    public class PluginUserData<T>
    {
        public Guid Id { get; set; }
        public T UserData { get; set; }
    }

    public class PluginService
    {
        private IPluginUserDataRepository userData;
        private IUnitOfWorkFactory uowFactory;
        private IPluginRepository plugins;

        public PluginService(ICmsDataProvider provider)
            : this(provider.GetUnitOfWorkFactory(), provider.GetService<IPluginUserDataRepository>(), provider.GetService<IPluginRepository>())
        {
        }

        public PluginService(IUnitOfWorkFactory uowFactory, IPluginUserDataRepository userData, IPluginRepository plugins)
        {
            this.userData = userData;
            this.uowFactory = uowFactory;
            this.plugins = plugins;
        }

        public IPluginUserDataRepository PluginUserDataRepository
        {
            get { return this.userData; }
        }

        public IPluginRepository Plugins
        {

            get { return this.plugins;  }
        }


        /// <summary>
        /// Get user data for a given plugin and entity
        /// </summary>
        /// <param name="plugin"></param>
        /// <param name="entity"></param>
        /// <param name="userDataType"></param>
        public T GetUserData<T>(IPlugin plugin, AggregateRoot entity, string userDataType)
            where T : class
        {
            if (plugin == null)
                throw new ArgumentNullException("plugin");

            if (string.IsNullOrEmpty(userDataType))
                throw new ArgumentException("userDataType");

            PluginUserData data = this.userData.FindPluginUserData(plugin, entity, userDataType);
            if (data != null)
                return data.UserData as T;
            else
                return null;
        }

        /// <summary>
        /// Get user data for a given plugin and entity
        /// </summary>
        /// <param name="plugin"></param>
        /// <param name="entity"></param>
        /// <param name="userDataType"></param>
        public T GetUserData<T>(Guid id)
            where T : class
        {
            PluginUserData data = this.userData[id];
            if (data != null)
                return data.UserData as T;
            else
                return null;
        }

        /// <summary>
        /// Delete the user data by ID
        /// </summary>
        /// <param name="id"></param>
        public void DeleteUserData(Guid id)
        {
            using(IUnitOfWork uow =this.uowFactory.Begin())
            {
                PluginUserData data = this.userData[id];
                if(data != null)
                    uow.RegisterDeleted(data);

                uow.Commit();
            }
        }

        /// <summary>
        /// Get user data for a given plugin and entity
        /// </summary>
        /// <param name="plugin"></param>
        /// <param name="entity"></param>
        /// <param name="userDataType"></param>
        public IEnumerable<PluginUserData<T>> GetAllUserData<T>(IPlugin plugin, AggregateRoot entity, string userDataType)
            where T : class
        {
            if (plugin == null)
                throw new ArgumentNullException("plugin");

            if (string.IsNullOrEmpty(userDataType))
                throw new ArgumentException("userDataType");

            return this.userData.FindAllPluginUserData(plugin, entity, userDataType).Select(pud => new PluginUserData<T>() { Id = pud.Id, UserData = (T)pud.UserData });
        }

        /// <summary>
        /// Set user data for the given plugin and entity
        /// </summary>
        /// <param name="plugin"></param>
        /// <param name="entity"></param>
        /// <param name="userDataType"></param>
        /// <param name="userData"></param>
        public PluginUserData SetUserData(IPlugin plugin, AggregateRoot entity, string userDataType, object userData, bool single)
        {
            if (plugin == null)
                throw new ArgumentNullException("plugin");

            if (string.IsNullOrEmpty(userDataType))
                throw new ArgumentException("userDataType");

            using (var uow = this.uowFactory.Begin())
            {
                PluginUserData userDataInstance = null;
                
                if(single)
                    userDataInstance = this.userData.FindPluginUserData(plugin, entity, userDataType);

                if (userDataInstance == null)
                {
                    userDataInstance = this.userData.CreateEntityInstance();
                    uow.RegisterAdded(userDataInstance);
                }
                else
                {
                    uow.RegisterChanged(userDataInstance);
                }

                userDataInstance.PluginId = plugin.Id;
                userDataInstance.EntityId = ((entity == null) ? null : (Guid?)entity.Id);
                userDataInstance.UserDataType = userDataType;
                userDataInstance.UserData = userData;

                uow.Commit();

                return userDataInstance;
            }
        }

        /// <summary>
        /// Set user data for the given plugin and entity
        /// </summary>
        /// <param name="plugin"></param>
        /// <param name="entity"></param>
        /// <param name="userDataType"></param>
        /// <param name="userData"></param>
        public void DeleteSingleUserData(IPlugin plugin, AggregateRoot entity, string userDataType)
        {
            if (plugin == null)
                throw new ArgumentNullException("plugin");

            if (string.IsNullOrEmpty(userDataType))
                throw new ArgumentException("userDataType");

            using (var uow = this.uowFactory.Begin())
            {
                PluginUserData userDataInstance = null;
                
                userDataInstance = this.userData.FindPluginUserData(plugin, entity, userDataType);
                if(userDataInstance != null)
                    uow.RegisterDeleted(userDataInstance);

                uow.Commit();
            }
        }

        /// <summary>
        /// Set user data for the given plugin and entity
        /// </summary>
        /// <param name="plugin"></param>
        /// <param name="entity"></param>
        /// <param name="userDataType"></param>
        /// <param name="userData"></param>
        public void SetUserData(Guid id, object userData)
        {
            using (var uow = this.uowFactory.Begin())
            {
                PluginUserData data = this.userData[id];
                data.UserData = userData;

                uow.RegisterChanged(data);

                uow.Commit();
            }
        }
    }
}
